﻿using ExcelParser.CodeGen;
using NPOI.SS.UserModel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace ExcelParser.Reader
{
    public class ExcelReader
    {
        public const string SUPPERTED_EXCEL_EXTENSION = "*.xls*.xlsx";

        private string m_configDirectory;
        private string m_serializeDirectory;
        private string m_namespace;

        public ExcelReader(string configDirectory, string serializeDirectory, string nspace)
        {
            m_configDirectory = configDirectory;
            m_serializeDirectory = serializeDirectory;
            m_namespace = nspace;
        }

        /// <summary>
        /// 加载所有excel
        /// </summary>
        /// <returns></returns>
        public List<ExcelSheet> LoadExcels()
        {
            List<ExcelSheet> sheets = new List<ExcelSheet>();

            var allConfigs = Directory.GetFiles(m_configDirectory, "*.*", SearchOption.TopDirectoryOnly).Where(s => !string.IsNullOrEmpty(Path.GetExtension(s)) && SUPPERTED_EXCEL_EXTENSION.Contains(Path.GetExtension(s).ToLower()) && !s.Contains("~"));
            foreach (var configPath in allConfigs)
            {
                string workBookName = Path.GetFileNameWithoutExtension(configPath);
                using (FileStream fs = new FileStream(configPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                {
                    IWorkbook workbook = WorkbookFactory.Create(fs);
                    for (int sheetIndex = 0; sheetIndex < workbook.NumberOfSheets; sheetIndex++)
                    {
                        ISheet sheet = workbook.GetSheetAt(sheetIndex);
                        if (sheet.PhysicalNumberOfRows == 0)
                            continue;

                        for (int rowIndex = sheet.FirstRowNum; rowIndex <= sheet.LastRowNum; rowIndex++)
                        {
                            IRow row = sheet.GetRow(rowIndex);
                            if (row == null)
                                continue;

                            ICell cell = row.GetCell(0);

                            //寻找以[Config]为标记的首行
                            if (cell == null ||
                                cell.CellType != CellType.String)
                                continue;

                            if (GetStringCellValue(cell).Trim() == "[Config]")
                            {
                                string className = GetStringCellValue(row.GetCell(1, MissingCellPolicy.CREATE_NULL_AS_BLANK));

                                if (!string.IsNullOrEmpty(className))
                                {
                                    ExcelSheet excelSheet = new ExcelSheet()
                                    {
                                        path = configPath,
                                        name = className,
                                        nspace = m_namespace,
                                        workbook = workbook,
                                        sheet = sheet,
                                        fieldTypeRow = sheet.GetRow(++rowIndex),
                                        fieldNameRow = sheet.GetRow(++rowIndex),
                                        fieldDescRow = sheet.GetRow(++rowIndex),
                                        startDataRowIndex = ++rowIndex,
                                        primaryKeys = new List<string>(),
                                    };

                                    int index = 2;
                                    while (true)
                                    {
                                        string primaryKeyValue = GetStringCellValue(row.GetCell(index++, MissingCellPolicy.CREATE_NULL_AS_BLANK));
                                        if (string.IsNullOrEmpty(primaryKeyValue))
                                            break;

                                        excelSheet.primaryKeys.Add(primaryKeyValue);
                                    }

                                    sheets.Add(excelSheet);
                                    break;
                                }
                            }
                        }
                    }
                    workbook.Close();
                }
            }

            return sheets;
        }

        public void ReadExcel(Assembly excelAssembly, List<ExcelSheet> sheets)
        {
            for (int i = 0; i < sheets.Count; i++)
            {
                ParseSheet(excelAssembly, sheets[i]);
            }
        }

        private void ParseSheet(Assembly excelAssembly, ExcelSheet sheet)
        {
            Type classType = excelAssembly.GetType(sheet.FullName);
            //clear old data
            MethodInfo clearMethod = classType.GetMethod("Clear");
            clearMethod.Invoke(null, new object[] { });

            object c = Activator.CreateInstance(classType);
            for (int i = sheet.startDataRowIndex; i <= sheet.sheet.LastRowNum; i++)
            {
                ParseRow(excelAssembly, sheet, sheet.sheet.GetRow(i));
            }

            //serialize
            MethodInfo serializeMethod = classType.GetMethod("Serialize");
            serializeMethod.Invoke(null, new object[] { Path.Combine(m_serializeDirectory, sheet.name + ".bytes") });
        }

        private void ParseRow(Assembly excelAssembly, ExcelSheet sheet, IRow dataRow)
        {
            if (dataRow == null)
                return;

            Type classType = excelAssembly.GetType(sheet.FullName);
            Type recordType = excelAssembly.GetType(sheet.FullName + "+Record");
            if (classType == null || recordType == null)
            {
                ConfigParser.ErrorLogger?.Invoke("解析错误,找不到类型:" + sheet.FullName);
                return;
            }

            object recordObj = Activator.CreateInstance(recordType);

            for (int i = sheet.fieldNameRow.FirstCellNum; i < sheet.fieldNameRow.LastCellNum; i++)
            {
                string fieldName = GetStringCellValue(sheet.fieldNameRow.GetCell(i, MissingCellPolicy.CREATE_NULL_AS_BLANK));
                ExcelField field = sheet.config.FindField(fieldName);
                if (field == null)
                    continue;

                string value = GetStringCellValue(dataRow.GetCell(i, MissingCellPolicy.CREATE_NULL_AS_BLANK));
                PropertyInfo propertyInfo = recordType.GetProperty(fieldName);
                field.ParseValue(sheet.config.Writer, recordObj, propertyInfo, null, propertyInfo.PropertyType, value);
            }

            MethodInfo addDataMethod = classType.GetMethod("AddData");
            addDataMethod.Invoke(null, new object[] { recordObj });
        }

        public static object GetCellValue(ICell cell)
        {
            if (cell == null)
                return null;
            switch (cell.CellType)
            {
                case CellType.Numeric:
                    return cell.NumericCellValue;
                case CellType.String:
                    return cell.StringCellValue;
                case CellType.Boolean:
                    return cell.BooleanCellValue;
                case CellType.Formula:
                    try
                    {
                        return cell.NumericCellValue;
                    }
                    catch
                    {
                        return cell.StringCellValue;
                    }
                default:
                    return null;
            }
        }


        public static string GetStringCellValue(ICell cell)
        {
            object value = GetCellValue(cell);
            if (value == null)
                return string.Empty;
            return value.ToString();
        }

        public static double GetNumericCellValue(ICell cell)
        {
            object value = GetCellValue(cell);
            if (value == null)
                return 0;

            if (value.GetType() == typeof(double))
            {
                return (double)value;
            }
            return 0;
        }
    }
}
